#include "SHT3x.h"

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "wifiiot_errno.h"
#include "wifiiot_gpio.h"
#include "wifiiot_gpio_ex.h"
#include "wifiiot_i2c.h"
#include "wifiiot_i2c_ex.h"

/* ---- CRC ---- */
#define POLYNOMIAL                  (0x131) // P(x) = x^8 + x^5 + x^4 + 1 = 1 0011 0001

static uint8_t      SHT3X_CalcCrc(uint8_t data[], uint8_t nbrOfBytes);
static unsigned int SHT3X_CheckCrc(uint8_t data[], uint8_t nbrOfBytes, uint8_t checksum);
/* ---- CRC ---- */

// 计算温湿度
static float SHT3X_CalcTemperature(uint16_t rawValue);
static float SHT3X_CalcHumidity(uint16_t rawValue);

// 发送一条指令 2字节 
static unsigned int SHT3X_WriteCommand(SHT3x *dev, etCommands command)
{
    unsigned int ret = WIFI_IOT_SUCCESS;
    uint8_t send_data[2] = { command >> 8 , command & 0xFF };// 交换高低字节

    if (NULL == dev) return WIFI_IOT_ERR_I2C_INVALID_PARAMETER;

    WifiIotI2cData sht3x_i2c_data = { 0 };
    sht3x_i2c_data.sendBuf = send_data;
    sht3x_i2c_data.sendLen = 2;
    
    // 传输数据
    ret = I2cWrite(dev->host, dev->slave << 1, &sht3x_i2c_data);

    return ret;
}

// 启动周期性测量
unsigned int SHT3X_StartPeriodicMeasurment(SHT3x *dev, 
    etRepeatability repeatability, etFrequency frequency)
{
    unsigned int ret = WIFI_IOT_SUCCESS;
    etCommands cmd = CMD_NONE;

    switch(repeatability)
    {
        case REPEATAB_LOW: // low repeatability
            switch(frequency)
            {
                case FREQUENCY_HZ5: // low repeatability, 0.5 Hz
                    cmd = CMD_MEAS_PERI_05_L;
                    break;
                case FREQUENCY_1HZ: // low repeatability, 1.0 Hz
                    cmd = CMD_MEAS_PERI_1_L;
                    break;
                case FREQUENCY_2HZ: // low repeatability, 2.0 Hz
                    cmd = CMD_MEAS_PERI_2_L;
                    break;
                case FREQUENCY_4HZ: // low repeatability, 4.0 Hz
                    cmd = CMD_MEAS_PERI_4_L;
                    break;
                case FREQUENCY_10HZ: // low repeatability, 10.0 Hz
                    cmd = CMD_MEAS_PERI_10_L;
                    break;
                default:
                    ret = WIFI_IOT_ERR_I2C_INVALID_PARAMETER;
                    break;
            }
            break;
        case REPEATAB_MEDIUM: // medium repeatability
            switch(frequency)
            {
                case FREQUENCY_HZ5: // medium repeatability, 0.5 Hz
                    cmd = CMD_MEAS_PERI_05_M;
                    break;
                case FREQUENCY_1HZ: // medium repeatability, 1.0 Hz
                    cmd = CMD_MEAS_PERI_1_M;
                    break;
                case FREQUENCY_2HZ: // medium repeatability, 2.0 Hz
                    cmd = CMD_MEAS_PERI_2_M;
                    break;
                case FREQUENCY_4HZ: // medium repeatability, 4.0 Hz
                    cmd = CMD_MEAS_PERI_4_M;
                    break;
                case FREQUENCY_10HZ: // medium repeatability, 10.0 Hz
                    cmd = CMD_MEAS_PERI_10_M;
                    break;
                default:
                    ret = WIFI_IOT_ERR_I2C_INVALID_PARAMETER;// 参数错误
                    break;
            }
            break;
        case REPEATAB_HIGH: // high repeatability
            switch(frequency)
            {
            case FREQUENCY_HZ5: // high repeatability, 0.5 Hz
                cmd = CMD_MEAS_PERI_05_H;
                break;
            case FREQUENCY_1HZ: // high repeatability, 1.0 Hz
                cmd = CMD_MEAS_PERI_1_H;
                break;
            case FREQUENCY_2HZ: // high repeatability, 2.0 Hz
                cmd = CMD_MEAS_PERI_2_H;
                break;
            case FREQUENCY_4HZ: // high repeatability, 4.0 Hz
                cmd = CMD_MEAS_PERI_4_H;
                break;
            case FREQUENCY_10HZ: // high repeatability, 10.0 Hz
                cmd = CMD_MEAS_PERI_10_H;
                break;
            default:
                ret = WIFI_IOT_ERR_I2C_INVALID_PARAMETER;// 参数错误
                break;
            }
            break;
        default:
            ret = WIFI_IOT_ERR_I2C_INVALID_PARAMETER;// 参数错误
            break;
    }

    // 发送指令
    if (WIFI_IOT_SUCCESS == ret)
    {
        ret = SHT3X_WriteCommand(dev, cmd);
    }

    return ret;
}

// 读取周期测量的数值
unsigned int SHT3X_ReadMeasurementBuffer(SHT3x *dev)
{
    unsigned int    ret = WIFI_IOT_SUCCESS;
    uint8_t         recv_data[6] = { 0 };// 接收缓冲区
    WifiIotI2cData  sht3x_i2c_data = { 0 };  // 发送结构体
    uint8_t         send_data[2] = { 0xE0, 0x00 };   // 读取测量的温湿度值 指令:0xE000

    // 检查结构体
    if (NULL == dev) return WIFI_IOT_ERR_I2C_INVALID_PARAMETER;
    
    // 传输数据
    sht3x_i2c_data.sendBuf = send_data;
    sht3x_i2c_data.sendLen = 2;
    sht3x_i2c_data.receiveBuf = recv_data;
    sht3x_i2c_data.receiveLen = 6;   
    ret = I2cWriteread(dev->host, dev->slave << 1, &sht3x_i2c_data);

    // CRC 校验
    if (WIFI_IOT_SUCCESS == ret)
    {
        ret |= SHT3X_CheckCrc(recv_data + 0, 2, recv_data[2]);// 校验 温度
        ret |= SHT3X_CheckCrc(recv_data + 3, 2, recv_data[5]);// 校验 湿度
    }

    // 计算温湿度
    if (WIFI_IOT_SUCCESS == ret)
    {
        dev->data.temperature    = SHT3X_CalcTemperature(recv_data[0] << 8 | recv_data[1]);
        dev->data.humidity       = SHT3X_CalcHumidity(recv_data[3] << 8 | recv_data[4]);
    }

    return ret;
}

// 计算CRC值
static uint8_t SHT3X_CalcCrc(uint8_t data[], uint8_t nbrOfBytes)
{
    uint8_t bit; // bit mask
    uint8_t crc = 0xFF; // calculated checksum
    uint8_t byteCtr; // byte counter
    
    // calculates 8-Bit checksum with given polynomial
    for(byteCtr = 0; byteCtr < nbrOfBytes; ++byteCtr)
    {
        crc ^= data[byteCtr];
        for(bit = 0; bit < 8; ++bit)
        {
            if(crc & 0x80) crc = (crc << 1) ^ POLYNOMIAL;
            else crc = (crc << 1);
        }
    }
    return crc;
}

// 校验CRC值
static unsigned int SHT3X_CheckCrc(uint8_t data[], uint8_t nbrOfBytes, uint8_t checksum)
{
    uint8_t crc; // calculated checksum
    
    // 计算校验值
    crc = SHT3X_CalcCrc(data, nbrOfBytes);
    
    // 比对校验值
    if(crc != checksum) return WIFI_IOT_FAILURE;// 校验错误
    else return WIFI_IOT_SUCCESS;// 校验成功
}

// 计算温度
static float SHT3X_CalcTemperature(uint16_t rawValue)
{
    // calculate temperature [°C]
    // T = -45 + 175 * rawValue / (2^16-1)
    return 175.0f * (float)rawValue / 65535.0f - 45.0f;
}

// 计算湿度
static float SHT3X_CalcHumidity(uint16_t rawValue)
{
    // calculate relative humidity [%RH]
    // RH = rawValue / (2^16-1) * 100
    return 100.0f * (float)rawValue / 65535.0f;
}
